<body></body>
<script>
  let activeEffect = null
  const effectStack = [] //effect栈
  const bucket = new WeakMap()
  //确保ITERATE_KEY是唯一，设置对象的迭代
  const ITERATE_KEY = Symbol()

  function track(target, key) {
    if (!activeEffect) return
    let depMap = bucket.get(target)
    if (!depMap) bucket.set(target, (depMap = new Map()))

    let deps = depMap.get(key)
    if (!deps) depMap.set(key, (deps = new Set()))

    const effectSet = deps.add(activeEffect)
    activeEffect.deps.push(effectSet)
  }

  // 增加第四个参数newVal
  function trigger(target, key, type, newVal) {
    let depMap = bucket.get(target)
    if (!depMap) return
    const effects = depMap.get(key)
    const effectsToRun = new Set()
    // ADD 或 DELETE 操作时，需要触发与ITERATE_KEY 相关联的副作用函数重新执行
    if (type === 'ADD' || type == 'DELETE') {
      const iterateEffects = depMap.get(ITERATE_KEY)
      iterateEffects &&
        iterateEffects.forEach(effectFn => {
          if (effectFn !== activeEffect) {
            effectsToRun.add(effectFn)
          }
        })
    }

    effects &&
      effects.forEach(effectFn => {
        if (effectFn !== activeEffect) {
          effectsToRun.add(effectFn)
        }
      })
    // 操作类型是数组的ADD，执行 length相关副作用
    if (type === 'ADD' && Array.isArray(target)) {
      console.log('add')

      const lengthEffect = depMap.get('length')
      lengthEffect &&
        lengthEffect.forEach(effectFn => {
          if (effectFn !== activeEffect) {
            effectsToRun.add(effectFn)
          }
        })
    }

    // 5、如果数组修改了 length 属性
    if (Array.isArray(target) && key === 'length') {
      // 将索引大于 or 等于新 length  的元素相关副作用函数添加到effectsToRun
      depMap.forEach((effects, key) => {
        if (typeof key === 'number' && newVal <= key) {
          //这里遍历的key是数组的索引，0，1，2，3
          effects.forEach(effectFn => {
            if (effectFn !== activeEffect) {
              effectsToRun.add(effectFn)
            }
          })
        }
      })
    }
    //
    effectsToRun.forEach(effectFn => {
      if (effectFn?.options?.scheduler) {
        effectFn?.options?.scheduler(effectFn)
      } else {
        effectFn()
      }
    })
  }

  function effect(fn, options) {
    // 这里的 effectFn是一个独立函数，就是每一次effect开辟一个新的空间，使用的是新的指针
    function effectFn() {
      cleanup(effectFn)
      activeEffect = effectFn

      effectStack.push(effectFn)
      const res = fn()
      effectStack.pop(effectFn)
      activeEffect = effectStack[effectStack.length - 1]
      return res
    }
    effectFn.options = options
    effectFn.deps = [] //反向收集 effectSet的引用
    if (!effectFn?.options?.lazy) effectFn()
    return effectFn
  }
  function cleanup(effectFn) {
    effectFn.deps.forEach(depSet => depSet.delete(effectFn))
    effectFn.deps.length = 0 //遍历所有的 Set集合删除同一个副作用
  }

  const jobQueue = new Set() // 任务队列（自动去重）
  let isFlushing = false // 队列刷新状态
  // 任务队列控制逻辑
  function flushJob() {
    if (isFlushing) return
    isFlushing = true // 微任务触发器
    Promise.resolve()
      .then(() => jobQueue.forEach(job => job()))
      .finally(() => (isFlushing = false))
  }

  // computed:把 getter 作为副作用函数，创建一个 lazy 的 effect
  function computed(getter) {
    let value // 缓存值
    let dirty = true // 标记是否需要重新计算

    const effectFn = effect(getter, {
      lazy: true, // 延迟执行
      scheduler() {
        dirty = true // 依赖变化时标记为需要重新计算
        trigger(obj, 'value') // 触发依赖更新
      }
    })

    const obj = {
      get value() {
        if (dirty) {
          value = effectFn() // 执行计算
          dirty = false // 标记为已计算
        }
        track(obj, 'value') // 追踪读取操作
        return value
      }
    }
    return obj
  }

  // 遍历 obj 对象及其所有嵌套属性
  function traverse(value, seen = new Set()) {
    if (typeof value !== 'object' || value === null || seen.has(value)) return
    seen.add(value) //标记它已被遍历
    for (const k in value) {
      traverse(value[k], seen)
    }
    return value
  }
  // watch 函数接收 3个参数，source 是响应式数据，cb是回调函数,options配置
  function watch(source, cb, options = {}) {
    let getter
    if (typeof source === 'function') getter = source
    else getter = () => traverse(source)
    let oldValue, newValue

    let cleanup //误区：这里的 oninvalidata不会watch直接调用，是开发者在watch使用过程去调用
    const oninvalidate = fn => {
      cleanup = fn
    }
    const job = () => {
      newValue = effectFn()
      if (cleanup) cleanup() //在回调函数cd之前，先调用注册过期回调
      cb(newValue, oldValue, oninvalidate) //将 onInvalidate 作为回调函数的第三个参数，以便用户使用
      oldValue = newValue
    }
    const effectFn = effect(() => getter(), {
      lazy: true,
      scheduler: job
    })
    if (options.immediate) job()
    else oldValue = effectFn()
  }

  // 1、定义个 Map 实例，存储原始对象到代理对象的映射
  const reactiveMap = new Map()

  // reactive
  function reactive(obj) {
    // 优先通过原始对象 obj 寻找之前创建的代理对象，如果找到了，直接返回已有的代理对象
    const existionProxy = reactiveMap.get(obj)
    if (existionProxy) return existionProxy

    // 否则，创建新的代理对象
    const proxy = createReactive(obj)
    // 存储到 Map 中，从而帮忙重复创建
    reactiveMap.set(obj, proxy)
    return proxy
  }
  // 浅响应
  function showReactive(obj) {
    return createReactive(obj, true)
  }
  // 深只读
  function readonly(obj) {
    return createReactive(obj, false, true) //原数据，是否浅响应，是否只读
  }
  // 浅只读
  function showReadonly(obj) {
    return createReactive(obj, true /*show*/, true)
  }

  const originMethod = Array.prototype.includes
  const arrayInstrumentations = {
    // 重写 includes
    includes: function (...args) {
      // this 是代理对象，先在代理对象中查找，将结果存储到 res 中
      let res = originMethod.apply(this, args)
      if (res === false) {
        ////////res 是 false 说明没有找到，通过 this.raw 拿到原始数组，再去其中查找并更新 res 指
        res = originMethod.apply(this.raw, args)
      }
      // 返回最终结果
      return res
    }
  }
  //封装 createReactive 函数，接收一个参数 isshallow，代表是否为浅响应，默认为 false，即非浅响应
  // isReadonly 只读
  function createReactive(obj, isShallow = false, isReadonly = false) {
    return new Proxy(obj, {
      get(target, key, receiver) {
        console.log(`get操作,${key}`)
        // 代理对象可以通过raw 属性访问原始数据
        if (key === 'raw') {
          return target
        }
        // 如果操作的目标是数组，并且 key 存在于 arrayInstrumentations上，
        // 那么返回定义 在 arrayInstrumentations 上的值
        if (Array.isArray(target) && arrayInstrumentations.hasOwnProperty(key)) {
          return Reflect.get(arrayInstrumentations, key, receiver)
        }
        if (!isReadonly && typeof key !== 'symbol') {
          //添加判断，如果 key的类型是 symbol ，则不进行追踪
          //性能问题2。.这样 **，性能问题**Symbol：如果 类型副作用的函数值无
          // 就不会条件无追意踪间引发副  作用Symbol保持了会代码导致不的必要的稳定副性作用触。
          // 发挥影响性能，因为 Symbol 通常不需要被追踪。
          track(target, key)
        }

        // 只读这里的副作用没有，改不了，删不了，不会变化，没有收集依赖的必要
        if (!isReadonly) {
          track(target, key)
        }

        // 得到原始值
        let res = Reflect.get(target, key, receiver)

        // 如果是浅响应，则直接返回原始值
        if (isShallow) {
          return res
        }

        if (typeof res === 'object' && res !== null) {
          //如果数据为只读，则调用readonly 对值进行包装
          // 如果值可以被代理，就返回代理对象
          return isReadonly ? readonly(res) : reactive(res)
        }

        // 返回 res
        return res
      },
      set(target, key, newVal, receiver) {
        if (isReadonly) {
          console.warn(`属性 ${key} 是只读的`)
          return true
        }
        // 先收集旧的值
        const oldVal = target[key]
        let type // 操作类型
        if (Array.isArray(target)) {
          // 判断数组代理：当前索引比长度 小 属于set，否则为ADD
          type = Number(key) < target.length ? 'SET' : 'ADD'
        } else {
          // 判断对象有某个属性(非原型链继承)，返回布尔值，仅添加时触发副作用
          type = Object.prototype.hasOwnProperty.call(target, key) ? 'SET' : 'ADD'
        }

        const res = Reflect.set(target, key, newVal, receiver)
        //target===receiver.raw说明receiver就是target的代理对象
        if (target === receiver.raw) {
          // 比较新值与旧值，只要当不全等的时候才触发响应
          if (oldVal !== newVal && (oldVal === oldVal || newVal === newVal)) {
            console.log(`set操作`)
            // 增加第四个参数，响应的新增
            trigger(target, key, type, newVal)
          }
        }
        return res
      },
      // has控制属性存在性 in
      has(target, key) {
        console.log(`in操作`)
        track(target, key)
        return Reflect.has(target, key)
      },
      // Object的拦截for...in、Object.key\ ownKeys控制属性的枚举顺序与内容
      ownKeys(target, key) {
        console.log('%c对象迭代', 'color: purple; font-size: 30px')
        track(target, Array.isArray(target) ? 'length' : ITERATE_KEY)
        return Reflect.ownKeys(target)
      },
      // 成功删除对象自己的属性时触发更新，删除不存在属性时返回 false
      deleteProperty(target, key) {
        if (isReadonly) {
          console.warn(`属性 ${key} 是只读的`)
          return true
        }
        const hadKey = Object.prototype.hasOwnProperty.call(target, key)
        const res = Reflect.deleteProperty(target, key)
        if (res && hadKey) {
          trigger(target, key, 'DELETE')
        }
        return res
      }
    })
  }

  // 没问题：
  // const arr = reactive([1, 2, 3, 4, 5])
  // effect(() => {
  //   console.log(arr.includes(1))
  // })
  // arr[0] = 3

  const obj = {}
  const arr = reactive([obj])
  console.log(arr.includes(obj)) //这里直接obj还是false
</script>
